﻿using System;
using System.CodeDom;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media;
using Sokoban.Model;
using Sokoban.ViewModel.Boxes;
using Brushes = System.Windows.Media.Brushes;
using System.Windows;
using System.Windows.Media.Imaging;

namespace Sokoban.ViewModel
{
    public class SokobanViewModel : INotifyPropertyChanged
    {
       
        public SokobanViewModel()
        {
            // load xml definition
            // BoxMatrix = new boxMatric[n][m]
        }

        public void LoadMap(string path)
        {
            var map = MapReader.ReadMap(path);
            bool isPlayerOnMap = false;

            Rows = map.GridMatrix.GetLength(0);
            Cols = map.GridMatrix.GetLength(1);
            BoxMatrix = new BoxViewModel[Rows, Cols];

            for (int i = 0; i < Rows; i++)
            {
                for (int j = 0; j < Cols; j++)
                {
                    var bvm = new BoxViewModel();
                    bvm.Box = map.GridMatrix[i, j];
                    bvm.Name = map.GridMatrix[i, j].ToString();
                    BoxMatrix[i, j] = bvm;

                    if (bvm.Name == "Player")
                    {
                        if (isPlayerOnMap)
                        {
                            throw new Exception("invalid count of player-boxes!");
                        }
                        playerIndexX = i;
                        playerIndexY = j;
                        isPlayerOnMap = true;
                    }
                }
            }
        }

        private void checkVictory()
        {
            bool victory = true;

            foreach (var item in BoxMatrix)
            {
                if (item.Box is Box)
                {
                    victory = item.Box.isOnGoal;

                    if (!victory)
                    {
                        return;
                    }
                }
            }

            MessageBox.Show("You won this time!");
        }

        private bool canMove(int xDir, int yDir, out int dist)
        {
            if (BoxMatrix[playerIndexX + xDir, playerIndexY + yDir].Box is Goal)
            {
                dist = 1;
                return true;
            }
            else
            {
                dist = 0;
                int tempX = playerIndexX;
                int tempY = playerIndexY;
                while (tempX + xDir >= 0 && tempX + xDir < BoxMatrix.GetLength(0) && tempY + yDir >= 0 && tempY + yDir < BoxMatrix.GetLength(1))
                {
                    tempX += xDir;
                    tempY += yDir;
                    ++dist;

                    if (BoxMatrix[tempX, tempY].Box is Wall || BoxMatrix[tempX, tempY].Box is Empty || BoxMatrix[tempX, tempY].Box is Goal)
                    {
                        if (BoxMatrix[tempX, tempY].Box is Empty || BoxMatrix[tempX, tempY].Box is Goal)
                        {
                            return true;
                        }
                        else
                        {
                            dist = 0;
                            return false;
                        }
                    }  
                }

                return false;
            }
        }


        public void MoveUp()
        {
            if (playerIndexX == 0)
            {
                return;
            }

            if (BoxMatrix[playerIndexX - 1, playerIndexY].Box is Wall)
            {
                return;
            }

            if (BoxMatrix[playerIndexX - 1, playerIndexY].Box is Empty)
            {               
                BoxMatrix[playerIndexX - 1, playerIndexY].Box = BoxMatrix[playerIndexX, playerIndexY].Box.Clone();
                if (BoxMatrix[playerIndexX, playerIndexY].Box.isOnGoal == true)
                {
                    BoxMatrix[playerIndexX, playerIndexY].Box = new Goal(playerIndexX, playerIndexY);
                }
                else
                {
                    BoxMatrix[playerIndexX, playerIndexY].Box = new Empty(playerIndexX, playerIndexY);
                }
                playerIndexX -= 1;
                BoxMatrix[playerIndexX, playerIndexY].Box.isOnGoal = false;
                return;
            }

            int distance = 0;
            if (canMove(-1, 0, out distance))
            {
                bool tempOnGoal = false;
                for (int i = playerIndexX - distance; i < playerIndexX; i++)
                {
                    if (BoxMatrix[i, playerIndexY].Box is Goal)
                    {
                        tempOnGoal = true;
                    }
                    else tempOnGoal = BoxMatrix[i, playerIndexY].Box.isOnGoal;

                    BoxMatrix[i, playerIndexY].Box = BoxMatrix[i + 1, playerIndexY].Box.Clone();
                    BoxMatrix[i, playerIndexY].Box.isOnGoal = tempOnGoal;
                }

                if (BoxMatrix[playerIndexX, playerIndexY].Box.isOnGoal == true)
                {
                    BoxMatrix[playerIndexX, playerIndexY].Box = new Goal(playerIndexX, playerIndexY);
                }
                else
                {
                    BoxMatrix[playerIndexX, playerIndexY].Box = new Empty(playerIndexX, playerIndexY);
                }

                playerIndexX -= 1;

                checkVictory();
            }
        }

        public void MoveDown()
        {
            if (playerIndexX == BoxMatrix.GetLength(0) - 1)
            {
                return;
            }

            if (BoxMatrix[playerIndexX + 1, playerIndexY].Box is Wall)
            {
                return;
            }

            if (BoxMatrix[playerIndexX + 1, playerIndexY].Box is Empty)
            {
                BoxMatrix[playerIndexX + 1, playerIndexY].Box = BoxMatrix[playerIndexX, playerIndexY].Box.Clone();
                if (BoxMatrix[playerIndexX, playerIndexY].Box.isOnGoal == true)
                {
                    BoxMatrix[playerIndexX, playerIndexY].Box = new Goal(playerIndexX, playerIndexY);
                }
                else
                {
                    BoxMatrix[playerIndexX, playerIndexY].Box = new Empty(playerIndexX, playerIndexY);
                }
                playerIndexX += 1;
                BoxMatrix[playerIndexX, playerIndexY].Box.isOnGoal = false;
                return;
            }

            int distance = 0;
            if (canMove(1, 0, out distance))
            {
                bool tempOnGoal = false;
                for (int i = playerIndexX + distance; i > playerIndexX; i--)
                {
                    if (BoxMatrix[i, playerIndexY].Box is Goal)
                    {
                        tempOnGoal = true;
                    }
                    else tempOnGoal = BoxMatrix[i, playerIndexY].Box.isOnGoal;

                    BoxMatrix[i, playerIndexY].Box = BoxMatrix[i - 1, playerIndexY].Box.Clone();
                    BoxMatrix[i, playerIndexY].Box.isOnGoal = tempOnGoal;
                }

                if (BoxMatrix[playerIndexX, playerIndexY].Box.isOnGoal == true)
                {
                    BoxMatrix[playerIndexX, playerIndexY].Box = new Goal(playerIndexX, playerIndexY);
                }
                else
                {
                    BoxMatrix[playerIndexX, playerIndexY].Box = new Empty(playerIndexX, playerIndexY);
                }

                playerIndexX += 1;

                checkVictory();
            }
        }

        public void MoveLeft()
        {
            if (playerIndexY == 0)
            {
                return;
            }

            if (BoxMatrix[playerIndexX, playerIndexY - 1].Box is Wall)
            {
                return;
            }

            if (BoxMatrix[playerIndexX, playerIndexY - 1].Box is Empty)
            {
                BoxMatrix[playerIndexX, playerIndexY - 1].Box = BoxMatrix[playerIndexX, playerIndexY].Box.Clone();
                if (BoxMatrix[playerIndexX, playerIndexY].Box.isOnGoal == true)
                {
                    BoxMatrix[playerIndexX, playerIndexY].Box = new Goal(playerIndexX, playerIndexY);
                    BoxMatrix[playerIndexX, playerIndexY].Box.isOnGoal = false;
                }
                else
                {
                    BoxMatrix[playerIndexX, playerIndexY].Box = new Empty(playerIndexX, playerIndexY);
                }
                playerIndexY -= 1;
                BoxMatrix[playerIndexX, playerIndexY].Box.isOnGoal = false;
                return;
            }

            int distance = 0;
            if (canMove(0, -1, out distance))
            {
                bool tempOnGoal = false;
                for (int i = playerIndexY - distance; i < playerIndexY; i++)
                {
                    if (BoxMatrix[playerIndexX, i].Box is Goal)
                    {
                        tempOnGoal = true;
                    }
                    else tempOnGoal = BoxMatrix[playerIndexX, i].Box.isOnGoal;

                    BoxMatrix[playerIndexX, i].Box = BoxMatrix[playerIndexX, i + 1].Box.Clone();
                    BoxMatrix[playerIndexX, i].Box.isOnGoal = tempOnGoal;
                }

                if (BoxMatrix[playerIndexX, playerIndexY].Box.isOnGoal == true)
                {
                    BoxMatrix[playerIndexX, playerIndexY].Box = new Goal(playerIndexX, playerIndexY);
                }
                else
                {
                    BoxMatrix[playerIndexX, playerIndexY].Box = new Empty(playerIndexX, playerIndexY);
                }
                
                playerIndexY -= 1;

                checkVictory();
            }
        }

        public void MoveRight()
        {
            if (playerIndexY == BoxMatrix.GetLength(1) - 1)
            {
                return;
            }

            if (BoxMatrix[playerIndexX, playerIndexY + 1].Box is Wall)
            {
                return;
            }

            if (BoxMatrix[playerIndexX, playerIndexY + 1].Box is Empty)
            {
                BoxMatrix[playerIndexX, playerIndexY + 1].Box = BoxMatrix[playerIndexX, playerIndexY].Box.Clone();
                if (BoxMatrix[playerIndexX, playerIndexY].Box.isOnGoal == true)
                {
                    BoxMatrix[playerIndexX, playerIndexY].Box = new Goal(playerIndexX, playerIndexY);
                }
                else
                {
                    BoxMatrix[playerIndexX, playerIndexY].Box = new Empty(playerIndexX, playerIndexY);
                }
                playerIndexY += 1;
                BoxMatrix[playerIndexX, playerIndexY].Box.isOnGoal = false;
                return;
            }

            int distance = 0;
            if (canMove(0, +1, out distance))
            {
                bool tempOnGoal = false;
                for (int i = playerIndexY + distance; i > playerIndexY; i--)
                {
                    if (BoxMatrix[playerIndexX, i].Box is Goal)
                    {
                        tempOnGoal = true;
                    }
                    else tempOnGoal = BoxMatrix[playerIndexX, i].Box.isOnGoal;
                    BoxMatrix[playerIndexX, i].Box = BoxMatrix[playerIndexX, i - 1].Box.Clone();
                    BoxMatrix[playerIndexX, i].Box.isOnGoal = tempOnGoal;
                }

                if (BoxMatrix[playerIndexX, playerIndexY].Box.isOnGoal == true)
                {
                    BoxMatrix[playerIndexX, playerIndexY].Box = new Goal(playerIndexX, playerIndexY);
                }
                else
                {
                    BoxMatrix[playerIndexX, playerIndexY].Box = new Empty(playerIndexX, playerIndexY);
                }

                playerIndexY += 1;

                checkVictory();
            }
        }

        public int Rows { get; private set; }
        public int Cols { get; private set; }

        private int playerIndexX;
        private int playerIndexY;

        public BoxViewModel[,] BoxMatrix;
   
        public event PropertyChangedEventHandler PropertyChanged;
    }

    public class BoxViewModel : INotifyPropertyChanged
    {
        public string Name
        {
            get { return myName; }
            set
            {
                myName = value;
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("Name"));
                }
                switch (Name)
                {
                    case "Player":                  
                        ImgSource = "pack://application:,,,/Resources/player.jpg";
                        break;
                    case "Empty":
                        ImgSource = "pack://application:,,,/Resources/empty.jpg";
                        break;
                    case "Wall":
                        ImgSource = "pack://application:,,,/Resources/wall.jpg";
                        break;
                    case "Box":
                        ImgSource = "pack://application:,,,/Resources/box.jpg";
                        break;
                    case "Goal":
                        ImgSource = "pack://application:,,,/Resources/goal.jpg";
                        break;
                    //default:
                    //    BackgroundColor = Brushes.DarkOrange;
                    //    break;
                }
            }
        }

        public SolidColorBrush BackgroundColor
        {
            get { return myBackgroundColor; }
            set
            {
                myBackgroundColor = value;

                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("BackgroundColor"));
                }
            }

        }
        public string ImgSource
        {
            get
            {
                return imgSource;
            }
            set
            {
                imgSource = value;
                if (PropertyChanged!=null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("ImgSource"));
                }
            }
        }

        public AbstractBox Box
        {
            get { return myBox; }
            set
            {
                myBox = value;
                Name = value.ToString();
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private string myName;
        private AbstractBox myBox;
        private SolidColorBrush myBackgroundColor;
        private string imgSource;
    }


}
